home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume12 / cnews / part13 < prev    next >
Encoding:
Internet Message Format  |  1987-10-22  |  43.5 KB

  1. Subject:  v12i038:  C News alpha release, Part13/14
  2. Newsgroups: comp.sources.unix
  3. Sender: sources
  4. Approved: rs@uunet.UU.NET
  5.  
  6. Submitted-by: utzoo!henry (Henry Spencer)
  7. Posting-number: Volume 12, Issue 38
  8. Archive-name: cnews/part13
  9.  
  10. #! /bin/sh
  11. # This is a shell archive.  Remove anything before this line, then unpack
  12. # it by saving it into a file and typing "sh file".  To overwrite existing
  13. # files, type "sh file -c".  You can also feed this as standard input via
  14. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  15. # will see the following message at the end:
  16. #        "End of archive 13 (of 14)."
  17. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  18. if test -f 'expire/expire.c' -a "${1}" != "-c" ; then 
  19.   echo shar: Will not clobber existing file \"'expire/expire.c'\"
  20. else
  21. echo shar: Extracting \"'expire/expire.c'\" \(20350 characters\)
  22. sed "s/^X//" >'expire/expire.c' <<'END_OF_FILE'
  23. X/*
  24. X * expire - expire old news
  25. X *
  26. X * One modest flaw:  links are not preserved in archived copies, i.e. you
  27. X * get multiple copies of multiply-posted articles.  Since link preservation
  28. X * is arbitrarily hard when control files get complex, to hell with it.
  29. X */
  30. X
  31. X#include <stdio.h>
  32. X#include <ctype.h>
  33. X#include <string.h>
  34. X#include <errno.h>
  35. X#include <time.h>
  36. X#include <sys/types.h>
  37. X#include <sys/timeb.h>
  38. X#include <sys/stat.h>
  39. X#include "news.h"
  40. X#include "newspaths.h"
  41. X
  42. X#ifndef EPOCH
  43. X#define    EPOCH    ((time_t)0)
  44. X#endif
  45. X
  46. X
  47. X/* structure for dbm */
  48. Xtypedef struct {
  49. X    char *dptr;
  50. X    int dsize;
  51. X} datum;
  52. X
  53. X#define    DAY    (24L*60L*60L)
  54. X
  55. X/* structure for expiry-control lists */
  56. Xstruct ctl {
  57. X    struct ctl *next;
  58. X    char *groups;        /* newsgroups */
  59. X    int ismod;        /* moderated? */
  60. X#        define    UNMOD    'u'
  61. X#        define    MOD    'm'
  62. X#        define    EITHER    'x'
  63. X    time_t retain;        /* earliest arrival date not expired */
  64. X    time_t normal;        /* earliest not expired in default case */
  65. X    time_t purge;        /* latest arrival date always expired */
  66. X    char *dir;        /* Archive dir or NULL. */
  67. X};
  68. X
  69. X/* header for internal form of control file */
  70. Xstruct ctl *ctls = NULL;
  71. Xstruct ctl *lastctl = NULL;
  72. X
  73. X/*
  74. X * Headers for lists by newsgroup, derived (mostly) from active file.
  75. X * Hashing is by length of newsgroup name; this is quick and works well,
  76. X * and there is no simple variation that does much better.
  77. X */
  78. X#define    NHASH    80
  79. Xstruct ctl *ngs[NHASH] = { NULL };
  80. X
  81. Xint debug = 0;            /* for inews routines */
  82. Xint expdebug = 0;        /* expire debugging */
  83. X
  84. Xint printexpiring = 0;        /* print info line for expiring articles? */
  85. Xchar *defarch = NULL;        /* default archive dir */
  86. X
  87. Xint fourfields = 0;        /* old 4-field C news history file? */
  88. X
  89. Xchar dont[] = "don't";        /* magic cookie for checkexpire() return */
  90. X
  91. Xtime_t now;
  92. Xstruct timeb ftnow;        /* ftime() result for getdate() */
  93. X
  94. Xchar subject[MAXLINE] = "";    /* buffer for subject line, minus header */
  95. X
  96. X/*
  97. X * Archive-copying buffer.
  98. X * 8KB buffer is large enough to take most articles at one gulp,
  99. X * and also large enough for virtual certainty of getting the
  100. X * Subject: line in the first bufferload.
  101. X */
  102. Xchar abuf[8*1024];
  103. X
  104. Xchar *progname;
  105. X
  106. Xextern int errno;
  107. Xextern long atol();
  108. Xextern double atof();
  109. Xextern char *malloc();
  110. Xextern struct tm *gmtime();
  111. Xextern time_t time();
  112. X
  113. Xextern time_t getdate();
  114. X
  115. X/* forwards */
  116. XFILE *eufopen();
  117. Xchar *enstring();
  118. Xchar *checkexpire();
  119. Xtime_t back();
  120. Xvoid checkdir();
  121. Xvoid errunlock();
  122. Xvoid control();
  123. Xvoid prime();
  124. Xvoid doit();
  125. Xvoid cd();
  126. Xvoid process();
  127. Xvoid warning();
  128. Xvoid printstuff();
  129. Xvoid expire();
  130. Xvoid mkparents();
  131. Xvoid getsubj();
  132. Xvoid refill();
  133. Xvoid printlists();
  134. Xvoid pctl();
  135. Xvoid fillin();
  136. Xvoid ctlline();
  137. X
  138. X/*
  139. X - main - parse arguments and handle options
  140. X */
  141. Xmain(argc, argv)
  142. Xint argc;
  143. Xchar *argv[];
  144. X{
  145. X    register int c;
  146. X    register int errflg = 0;
  147. X    register struct ctl *ct;
  148. X    register FILE *cf;
  149. X    extern int optind;
  150. X    extern char *optarg;
  151. X
  152. X    progname = argv[0];
  153. X    now = time((time_t *)NULL);
  154. X    ftime(&ftnow);
  155. X
  156. X    while ((c = getopt(argc, argv, "pa:od")) != EOF)
  157. X        switch (c) {
  158. X        case 'p':    /* print info line for archived articles */
  159. X            printexpiring = 1;
  160. X            break;
  161. X        case 'a':    /* archive in this directory */
  162. X            defarch = optarg;
  163. X            break;
  164. X        case 'o':    /* old 4-field history file */
  165. X            fourfields = 1;
  166. X            break;
  167. X        case 'd':    /* debug */
  168. X            expdebug = 1;
  169. X            break;
  170. X        case '?':
  171. X        default:
  172. X            errflg++;
  173. X            break;
  174. X        }
  175. X    if (errflg || optind < argc-1) {
  176. X        fprintf(stderr, "Usage: %s [-p] [-a archdir] [ctlfile]\n",
  177. X                                progname);
  178. X        exit(2);
  179. X    }
  180. X
  181. X    if (optind < argc) {
  182. X        cf = eufopen(argv[optind], "r");
  183. X        control(cf);
  184. X        fclose(cf);
  185. X    } else
  186. X        control(stdin);
  187. X
  188. X    prime(ctlfile("active"));
  189. X
  190. X    if (defarch != NULL)
  191. X        checkdir(defarch);
  192. X
  193. X    if (expdebug)
  194. X        printlists();
  195. X
  196. X    (void) umask(newsumask());
  197. X    doit();            /* side effect: newslock() */
  198. X    newsunlock();
  199. X    exit(0);
  200. X}
  201. X
  202. X/*
  203. X - control - pick up a control file
  204. X */
  205. Xvoid
  206. Xcontrol(f)
  207. Xregister FILE *f;
  208. X{
  209. X    char ctl[MAXLINE];
  210. X    void ctlline();
  211. X
  212. X    while (fgets(ctl, sizeof(ctl), f) != NULL) {
  213. X        ctl[strlen(ctl)-1] = '\0';    /* get rid of newline */
  214. X        ctlline(ctl);
  215. X    }
  216. X}
  217. X
  218. X/*
  219. X - ctlline - process one control-file line
  220. X */
  221. Xvoid
  222. Xctlline(ctl)
  223. Xchar *ctl;
  224. X{
  225. X    register struct ctl *ct;
  226. X    char *field[4];
  227. X    char datebuf[50];
  228. X    char *dates[3];
  229. X    register int nf;
  230. X    int ndates;
  231. X
  232. X    errno = 0;
  233. X    ct = (struct ctl *)malloc(sizeof(struct ctl));
  234. X    if (ct == NULL)
  235. X        errunlock("out of memory for control list", "");
  236. X
  237. X    errno = 0;
  238. X    nf = split(ctl, field, 4, "\t ");
  239. X    if (nf != 4)
  240. X        errunlock("bad control line: `%s...'", ctl);
  241. X
  242. X    ct->groups = enstring(field[0]);
  243. X    if (STREQ(field[1], "m"))
  244. X        ct->ismod = MOD;
  245. X    else if (STREQ(field[1], "u"))
  246. X        ct->ismod = UNMOD;
  247. X    else if (STREQ(field[1], "x"))
  248. X        ct->ismod = EITHER;
  249. X    else
  250. X        errunlock("strange mod field `%s' in control file", field[1]);
  251. X
  252. X    if (strlen(field[2]) > sizeof(datebuf)-1)
  253. X        errunlock("date specification `%s' too long", field[2]);
  254. X    (void) strcpy(datebuf, field[2]);
  255. X    ndates = split(datebuf, dates, 3, "-");
  256. X    switch (ndates) {
  257. X    case 3:
  258. X        ct->retain = back(atof(dates[0]));
  259. X        ct->normal = back(atof(dates[1]));
  260. X        ct->purge = back(atof(dates[2]));
  261. X        break;
  262. X    case 2:
  263. X        ct->retain = back(0.0);
  264. X        ct->normal = back(atof(dates[0]));
  265. X        ct->purge = back(atof(dates[1]));
  266. X        break;
  267. X    case 1:
  268. X        ct->retain = back(0.0);
  269. X        ct->normal = back(atof(dates[0]));
  270. X        ct->purge = EPOCH;
  271. X        break;
  272. X    default:
  273. X        errunlock("date processing foulup in `%s'", field[1]);
  274. X        /* NOTREACHED */
  275. X        break;
  276. X    }
  277. X    if (ct->retain < ct->normal || ct->normal < ct->purge)
  278. X        errunlock("preposterous dates: `%s'", field[2]);
  279. X
  280. X    if (STREQ(field[3], "-"))
  281. X        ct->dir = NULL;
  282. X    else if (STREQ(field[3], "@")) {
  283. X        if (defarch == NULL)
  284. X            errunlock("@ in control file but no -a", "");
  285. X        ct->dir = defarch;
  286. X    } else {
  287. X        ct->dir = enstring(field[3]);
  288. X        checkdir(ct->dir);
  289. X    }
  290. X
  291. X    /* put on end of list */
  292. X    ct->next = NULL;
  293. X    if (lastctl == NULL)
  294. X        ctls = ct;
  295. X    else
  296. X        lastctl->next = ct;
  297. X    lastctl = ct;
  298. X}
  299. X
  300. X/*
  301. X - prime - prime control lists from active file
  302. X */
  303. Xvoid
  304. Xprime(afile)
  305. Xchar *afile;
  306. X{
  307. X    char buf[MAXLINE];
  308. X    register FILE *af;
  309. X    register struct ctl *ct;
  310. X#    define    NFACT    4
  311. X    char *field[NFACT];
  312. X    int nf;
  313. X    register int hash;
  314. X    void fillin();
  315. X
  316. X    af = eufopen(afile, "r");
  317. X    while (fgets(buf, sizeof(buf), af) != NULL) {
  318. X        nf = split(buf, field, NFACT, " \t");
  319. X        if (nf != NFACT)
  320. X            errunlock("bad active-file line for `%s'", field[0]);
  321. X        ct = (struct ctl *)malloc(sizeof(struct ctl));
  322. X        if (ct == NULL)
  323. X            errunlock("out of memory at newsgroup `%s'", field[0]);
  324. X        ct->groups = enstring(field[0]);
  325. X        ct->ismod = (strchr(field[3], 'm') != NULL) ? MOD : UNMOD;
  326. X        fillin(ct);
  327. X        hash = strlen(field[0]);
  328. X        if (hash > NHASH-1)
  329. X            hash = NHASH-1;
  330. X        ct->next = ngs[hash];
  331. X        ngs[hash] = ct;
  332. X    }
  333. X    (void) fclose(af);
  334. X}
  335. X
  336. X/*
  337. X - fillin - fill in a ctl struct for a newsgroup from the control-file list
  338. X */
  339. Xvoid
  340. Xfillin(ct)
  341. Xregister struct ctl *ct;
  342. X{
  343. X    register struct ctl *cscan;
  344. X
  345. X    for (cscan = ctls; cscan != NULL; cscan = cscan->next)
  346. X        if (ngmatch(cscan->groups, ct->groups) &&
  347. X                (cscan->ismod == ct->ismod ||
  348. X                        cscan->ismod == EITHER)) {
  349. X            ct->retain = cscan->retain;
  350. X            ct->normal = cscan->normal;
  351. X            ct->purge = cscan->purge;
  352. X            ct->dir = cscan->dir;
  353. X            return;
  354. X        }
  355. X
  356. X    /* oooooops... */
  357. X    errunlock("group `%s' not covered by control file", ct->groups);
  358. X}
  359. X
  360. X/*
  361. X - doit - file manipulation and master control
  362. X */
  363. Xvoid
  364. Xdoit()
  365. X{
  366. X    register int old;
  367. X    register FILE *new;
  368. X    struct stat stbuf;
  369. X    char buf[MAXLINE];
  370. X    char name[MAXLINE];
  371. X    long here;
  372. X    register char *nameend;
  373. X    datum lhs;
  374. X    datum rhs;
  375. X    register int ret;
  376. X
  377. X    cd(ctlfile((char *)NULL));
  378. X    old = open("history", 0);
  379. X    if (old < 0)
  380. X        errunlock("cannot open `%s'", "history");
  381. X    errno = 0;
  382. X    if (stat("history.n", &stbuf) >= 0)
  383. X        errunlock("disaster -- history.n already exists!", "");
  384. X    new = eufopen("history.n", "w");
  385. X    fclose(eufopen("history.n.dir", "w"));
  386. X    fclose(eufopen("history.n.pag", "w"));
  387. X    if (dbminit("history.n") < 0)
  388. X        errunlock("dbminit(history.n) failed", "");
  389. X
  390. X    cd(artfile((char *)NULL));
  391. X    while (readline(buf, sizeof(buf), old) >= 0) {
  392. X        process(buf);
  393. X        if (buf[0] != '\0') {
  394. X            register char *p;
  395. X
  396. X            /* extract the message-id, lowercase it */
  397. X            (void) strcpy(name, buf);
  398. X            nameend = strchr(name, '\t');
  399. X            if (nameend == NULL) {
  400. X                errno = 0;
  401. X                errunlock("bad return from process(): `%s'",
  402. X                                    name);
  403. X            }
  404. X            *nameend = '\0';
  405. X            for (p = name; *p != '\0'; p++)
  406. X                if (isascii(*p) && isupper(*p))
  407. X                    *p = tolower(*p);
  408. X
  409. X            /* make the DBM entry */
  410. X            lhs.dptr = name;
  411. X            lhs.dsize = strlen(name)+1;
  412. X            here = ftell(new);
  413. X            rhs.dptr = (char *)&here;
  414. X            rhs.dsize = sizeof(here);
  415. X            errno = 0;
  416. X            ret = store(lhs, rhs);
  417. X            if (ret < 0)
  418. X                errunlock("dbm failure on `%s'", name);
  419. X
  420. X            /* and the history entry */
  421. X            fputs(buf, new);
  422. X            putc('\n', new);
  423. X        }
  424. X    }
  425. X    /* side effect of readline() < 0:  newslock() */
  426. X
  427. X    close(old);
  428. X    fclose(new);
  429. X
  430. X    cd(ctlfile((char *)NULL));
  431. X    (void) unlink("history.o");
  432. X    if (link("history", "history.o") < 0)
  433. X        errunlock("can't move history", "");
  434. X    if (unlink("history") < 0)
  435. X        errunlock("can't finish moving history", "");
  436. X    if (link("history.n", "history") < 0)
  437. X        errunlock("disaster -- can't reinstate history!", "");
  438. X    if (unlink("history.n") < 0)
  439. X        errunlock("disaster -- can't unlink history.n!", "");
  440. X    if (unlink("history.dir") < 0)
  441. X        errunlock("disaster -- can't unlink history.dir!", "");
  442. X    if (unlink("history.pag") < 0)
  443. X        errunlock("disaster -- can't unlink history.pag!", "");
  444. X    if (link("history.n.dir", "history.dir") < 0)
  445. X        errunlock("disaster -- can't reinstate history.dir!", "");
  446. X    if (link("history.n.pag", "history.pag") < 0)
  447. X        errunlock("disaster -- can't reinstate history.pag!", "");
  448. X    if (unlink("history.n.dir") < 0)
  449. X        errunlock("disaster -- can't unlink history.n.dir!", "");
  450. X    if (unlink("history.n.pag") < 0)
  451. X        errunlock("disaster -- can't unlink history.n.pag!", "");
  452. X}
  453. X
  454. X/*
  455. X - process - handle one history line, modifying it if appropriate
  456. X *
  457. X * The line as supplied is modified if necessary, so that whatever
  458. X * is in that buffer when process() returns is what should be written
  459. X * to the new history file.  An empty string ("") means "write nothing".
  460. X */
  461. Xvoid
  462. Xprocess(line)
  463. Xchar *line;
  464. X{
  465. X    char work[MAXLINE];
  466. X#    define    NF    4    /* number of fields in history line */
  467. X    char *field[NF];
  468. X    register int nf;
  469. X    char keepnames[MAXLINE];
  470. X#    define    NN    50    /* number of pathnames for article */
  471. X    char *names[NN];
  472. X    register int nn;
  473. X    register char *dir;
  474. X    time_t recdate;
  475. X    time_t expdate;
  476. X    register int i;
  477. X    register char *scan;
  478. X    char delim;
  479. X
  480. X    if (expdebug)
  481. X        fprintf(stderr, "\nprocess(%s)\n", line);
  482. X    (void) strcpy(work, line);
  483. X    nf = split(work, field, NF, "\t");
  484. X    if (!fourfields && nf == NF-1) {
  485. X        field[3] = field[2];
  486. X        scan = strchr(field[1], '~');
  487. X        if (scan == NULL)
  488. X            errunlock("bad date format `%s'", field[1]);
  489. X        *scan++ = '\0';
  490. X        field[2] = scan;
  491. X        nf++;
  492. X    }
  493. X    if (nf != NF) {
  494. X        errno = 0;
  495. X        warning("invalid history line: `%s'", line);
  496. X        return;        /* leaving the line in the new history file */
  497. X    }
  498. X    if (strspn(field[1], "0123456789") == strlen(field[1]))
  499. X        recdate = atol(field[1]);
  500. X    else
  501. X        recdate = getdate(field[1], &ftnow);
  502. X    if (strspn(field[2], "0123456789") == strlen(field[2]))
  503. X        expdate = atol(field[2]);
  504. X    else if (!STREQ(field[2], "-") && !STREQ(field[2], "X"))
  505. X        expdate = getdate(field[2], &ftnow);
  506. X    else
  507. X        expdate = 0;    /* any old value */
  508. X    if (expdebug)
  509. X        fprintf(stderr, "rec %ld, expire %ld\n", recdate, expdate);
  510. X
  511. X    nn = split(field[3], names, NN, " ,");
  512. X    if (nn > NN) {
  513. X        errno = 0;
  514. X        warning("too many cross-postings in line `%s'", line);
  515. X        return;        /* leaving line in history file */
  516. X    }
  517. X
  518. X    keepnames[0] = '\0';
  519. X    for (i = 0; i < nn; i++) {
  520. X        if (expdebug)
  521. X            fprintf(stderr, "link %s\n", names[i]);
  522. X        dir = checkexpire(field, recdate, expdate, names[i]);
  523. X        if (dir != dont) {
  524. X            if (expdebug)
  525. X                fprintf(stderr, "expire into %s\n",
  526. X                    (dir == NULL) ? "(null)" : dir);
  527. X            for (scan = strchr(names[i], '.'); scan != NULL;
  528. X                    scan = strchr(scan+1, '.'))
  529. X                *scan = '/';
  530. X            expire(names[i], dir);
  531. X            if (dir != NULL && printexpiring)
  532. X                printstuff(field, names[i], recdate);
  533. X        } else {
  534. X            (void) strcat(keepnames, " ");
  535. X            (void) strcat(keepnames, names[i]);
  536. X        }
  537. X    }
  538. X
  539. X    delim = (fourfields) ? '\t' : '~';
  540. X    if (keepnames[0] != '\0') {
  541. X        if (STREQ(field[2], "X") || STREQ(field[2], "-"))
  542. X            sprintf(line, "%s\t%ld%c%s\t%s", field[0], recdate,
  543. X                    delim, field[2], keepnames+1);
  544. X        else
  545. X            sprintf(line, "%s\t%ld%c%ld\t%s", field[0], recdate,
  546. X                    delim, expdate, keepnames+1);
  547. X    } else
  548. X        line[0] = '\0';
  549. X
  550. X    if (expdebug)
  551. X        fprintf(stderr, "new line `%s'\n", line);
  552. X}
  553. X
  554. X/*
  555. X - checkexpire - should this article get expired?
  556. X *
  557. X * The "dont" variable's address is used as the don't-expire return value,
  558. X * since NULL is a legitimate archive-directory value internally.
  559. X *
  560. X * The check for an expiry date of 'X' is to support a possible future
  561. X * scheme in which the dbm file is not rebuilt every time.
  562. X */
  563. Xchar *                /* archive directory, NULL, or dont */
  564. Xcheckexpire(field, recdate, expdate, name)
  565. Xchar *field[];
  566. Xtime_t recdate;
  567. Xtime_t expdate;
  568. Xchar *name;
  569. X{
  570. X    char group[MAXFILE];
  571. X    register char *scan;
  572. X    register struct ctl *ct;
  573. X    time_t when;
  574. X    register int hash;
  575. X
  576. X    if (STREQ(field[2], "X"))
  577. X        return(dont);    /* vestigial entry */
  578. X
  579. X    (void) strcpy(group, name);
  580. X    scan = strchr(group, '/');
  581. X    if (*scan == NULL) {
  582. X        errno = 0;
  583. X        errunlock("bad format in article path `%s'", name);
  584. X    } else
  585. X        *scan = '\0';
  586. X
  587. X    /* find applicable expiry-control struct (make it if necessary) */
  588. X    hash = strlen(group);
  589. X    if (hash > NHASH-1)
  590. X        hash = NHASH-1;
  591. X    for (ct = ngs[hash]; ct != NULL && !STREQ(ct->groups, group);
  592. X                                ct = ct->next)
  593. X        continue;
  594. X    if (ct == NULL) {    /* oops, there wasn't one */
  595. X        if (expdebug)
  596. X            fprintf(stderr, "new group `%s'\n", group);
  597. X        ct = (struct ctl *)malloc(sizeof(struct ctl));
  598. X        if (ct == NULL)
  599. X            errunlock("out of memory for newsgroup `%s'", group);
  600. X        ct->groups = enstring(group);
  601. X        ct->ismod = UNMOD;    /* unknown -- treat it as mundane */
  602. X        fillin(ct);
  603. X        ct->next = ngs[hash];
  604. X        ngs[hash] = ct;
  605. X    }
  606. X
  607. X    /* decide whether it ought to expire */
  608. X    if (recdate >= ct->retain)    /* within retention period */
  609. X        return(dont);
  610. X    if (recdate <= ct->purge)    /* past purge date */
  611. X        return(ct->dir);
  612. X    if (!STREQ(field[2], "-")) {    /* explicit expiry date */
  613. X        if (now >= expdate)    /* past its explicit date */
  614. X            return(ct->dir);
  615. X        else
  616. X            return(dont);
  617. X    } else {
  618. X        if (recdate < ct->normal)    /* past default date */
  619. X            return(ct->dir);
  620. X        else
  621. X            return(dont);
  622. X    }
  623. X}
  624. X
  625. X/*
  626. X - expire - expire an article
  627. X */
  628. Xvoid
  629. Xexpire(name, dir)
  630. Xchar *name;
  631. Xchar *dir;
  632. X{
  633. X    char old[MAXFILE];
  634. X    char new[MAXFILE];
  635. X    struct stat stbuf;
  636. X
  637. X    (void) strcpy(old, artfile(name));
  638. X    if (dir != NULL) {
  639. X        sprintf(new, "%s/%s", dir, name);
  640. X        /* cp() usually succeeds, so try it before getting fancy */
  641. X        if (cp(old, new) < 0) {
  642. X            if (stat(old, &stbuf) < 0)
  643. X                return;        /* nonexistent */
  644. X            mkparents(name, dir);
  645. X            if (cp(old, new) < 0) {
  646. X                warning("can't archive `%s'", name);
  647. X                return;        /* without removing it */
  648. X            }
  649. X        }
  650. X    }
  651. X    if (unlink(old) < 0 && errno != ENOENT)
  652. X        warning("can't remove `%s'", name);
  653. X}
  654. X
  655. X/*
  656. X - cp - try to copy an article
  657. X */
  658. Xint                /* 0 success, -1 failure */
  659. Xcp(src, dst)
  660. Xchar *src;            /* absolute pathnames */
  661. Xchar *dst;
  662. X{
  663. X    register int ret;
  664. X    register int count;
  665. X    register int in, out;
  666. X    register int first = 1;
  667. X
  668. X    in = open(src, 0);
  669. X    if (in < 0)
  670. X        return(-1);
  671. X    out = creat(dst, 0666);
  672. X    if (out < 0) {
  673. X        close(in);
  674. X        return(-1);
  675. X    }
  676. X
  677. X    while ((count = read(in, abuf, sizeof(abuf))) > 0) {
  678. X        ret = write(out, abuf, count);
  679. X        if (ret != count)
  680. X            errunlock("write error in copying `%s'", src);
  681. X        if (first) {
  682. X            getsubj(abuf, count);
  683. X            first = 0;
  684. X        }
  685. X    }
  686. X    if (count < 0)
  687. X        errunlock("read error in copying `%s'", src);
  688. X
  689. X    close(in);
  690. X    close(out);
  691. X    return(0);
  692. X}
  693. X
  694. X/*
  695. X - getsubj - try to find the Subject: line in a buffer
  696. X *
  697. X * Result goes in "subject", and is never empty.  Tabs become spaces,
  698. X * since they are the output delimiters.
  699. X */
  700. Xvoid
  701. Xgetsubj(buf, bsize)
  702. Xchar *buf;
  703. Xint bsize;
  704. X{
  705. X    register char *scan;
  706. X    register char *limit;
  707. X    register int len;
  708. X    register int clipped;
  709. X    static char sline[] = "Subject:";
  710. X
  711. X    len = strlen(sline);
  712. X    limit = buf + bsize - len;
  713. X    for (scan = buf; scan < limit; scan++)
  714. X        if (STREQN(scan, sline, len) &&
  715. X                (scan == buf || *(scan-1) == '\n')) {
  716. X            scan += len;
  717. X            for (limit = scan; limit < buf+bsize; limit++)
  718. X                if (*limit == '\n')
  719. X                    break;
  720. X            while (scan < limit && isspace(*scan))
  721. X                scan++;
  722. X            len = limit-scan;
  723. X            clipped = 0;
  724. X            if (len > sizeof(subject)-1) {
  725. X                len = sizeof(subject) - 1 - strlen("...");
  726. X                clipped = 1;
  727. X            }
  728. X            if (len > 0) {
  729. X                (void) strncpy(subject, scan, len);
  730. X                subject[len] = '\0';
  731. X            } else
  732. X                (void) strcpy(subject, "???");
  733. X            if (clipped)
  734. X                (void) strcat(subject, "...");
  735. X            for (scan = strchr(subject, '\t'); scan != NULL;
  736. X                    scan = strchr(scan+1, '\t'))
  737. X                *scan = ' ';
  738. X            return;
  739. X        } else if (*scan == '\n' && scan+1 < limit && *(scan+1) == '\n')
  740. X            break;        /* empty line terminates header */
  741. X
  742. X    /* didn't find one -- fill in *something* */
  743. X    (void) strcpy(subject, "???");
  744. X}
  745. X
  746. X/*
  747. X - mkparents - try to make directories for archiving an article
  748. X */
  749. Xvoid
  750. Xmkparents(art, dir)
  751. Xchar *art;            /* name relative to dir */
  752. Xchar *dir;
  753. X{
  754. X    char name[MAXFILE];
  755. X    char cmd[MAXLINE];
  756. X    register char *p;
  757. X    register int len;
  758. X    static char quiet[] = " >/dev/null 2>/dev/null";
  759. X
  760. X    (void) strcpy(name, art);
  761. X    sprintf(cmd, "cd %s; PATH=/bin:/usr/bin mkdir ", dir);
  762. X    len = strlen(cmd) + sizeof(quiet);
  763. X    for (p = strchr(name, '/'); p != NULL; p = strchr(p+1, '/')) {
  764. X        *p = '\0';
  765. X        if (len + (p - name) + 1 >= sizeof(cmd)-1)
  766. X            errunlock("line overflow in mkdiring for `%s'", art);
  767. X        (void) strcat(cmd, name);
  768. X        (void) strcat(cmd, " ");
  769. X        *p = '/';
  770. X    }
  771. X    (void) strcat(cmd, quiet);
  772. X    (void) system(cmd);
  773. X}
  774. X
  775. Xchar *months[12] = {
  776. X    "Jan",
  777. X    "Feb",
  778. X    "Mar",
  779. X    "Apr",
  780. X    "May",
  781. X    "Jun",
  782. X    "Jul",
  783. X    "Aug",
  784. X    "Sep",
  785. X    "Oct",
  786. X    "Nov",
  787. X    "Dec",
  788. X};
  789. X
  790. X/*
  791. X - printstuff - print information about an expiring article
  792. X */
  793. Xvoid
  794. Xprintstuff(field, name, recdate)
  795. Xchar *field[];
  796. Xchar *name;
  797. Xtime_t recdate;
  798. X{
  799. X    struct tm *gmt;
  800. X
  801. X    gmt = gmtime(&recdate);
  802. X    printf("%s\t%s\t%d-%s-%d\t%s\n", name, field[1], gmt->tm_mday,
  803. X            months[gmt->tm_mon], gmt->tm_year+1900, subject);
  804. X}
  805. X
  806. X/*
  807. X - split - divide a line into fields, like awk split()
  808. X */
  809. Xint                /* number of fields */
  810. Xsplit(line, fields, nfmax, sep)
  811. Xchar *line;
  812. Xchar *fields[];
  813. Xint nfmax;
  814. Xchar *sep;
  815. X{
  816. X    register int i;
  817. X    register char *p;
  818. X
  819. X    i = 0;
  820. X    for (p = strtok(line, sep); p != NULL; p = strtok((char *)NULL, sep)) {
  821. X        if (i < nfmax)
  822. X            fields[i] = p;
  823. X        i++;
  824. X    }
  825. X
  826. X    return(i);
  827. X}
  828. X
  829. X/*
  830. X - eufopen - fopen, with errunlock if doesn't succeed
  831. X */
  832. XFILE *
  833. Xeufopen(name, mode)
  834. Xchar *name;
  835. Xchar *mode;
  836. X{
  837. X    FILE *f;
  838. X
  839. X    f = fopen(name, mode);
  840. X    if (f == NULL)
  841. X        errunlock("can't open `%s'", name);
  842. X    return(f);
  843. X}
  844. X
  845. X/*
  846. X - checkdir - check that a directory is real, writeable, and full pathname
  847. X */
  848. Xvoid                /* errunlock() if not */
  849. Xcheckdir(dir)
  850. Xchar *dir;
  851. X{
  852. X    struct stat stbuf;
  853. X
  854. X    if (stat(dir, &stbuf) < 0 || (stbuf.st_mode&S_IFMT) != S_IFDIR)
  855. X        errunlock("`%s' is not a directory", dir);
  856. X    if (access(dir, 02) < 0)
  857. X        errunlock("directory `%s' not writeable", dir);
  858. X    errno = 0;
  859. X    if (dir[0] != '/')
  860. X        errunlock("directory `%s' not an absolute pathname", dir);
  861. X}
  862. X
  863. X/*
  864. X - enstring - malloc space for a string
  865. X */
  866. Xchar *                /* errunlock() if malloc fails */
  867. Xenstring(s)
  868. Xchar *s;
  869. X{
  870. X    register char *p;
  871. X
  872. X    errno = 0;
  873. X    p = malloc((unsigned)strlen(s)+1);    /* +1 for NUL */
  874. X    if (p == NULL)
  875. X        errunlock("cannot malloc for `%s'", s);
  876. X    (void) strcpy(p, s);
  877. X    return(p);
  878. X}
  879. X
  880. X/*
  881. X - back - get a date n days back, with overflow check
  882. X *
  883. X * Requires that "now" be set first.
  884. X */
  885. Xtime_t
  886. Xback(ndays)
  887. Xdouble ndays;
  888. X{
  889. X    if (ndays > now / DAY)    /* past beginning of time */
  890. X        return((time_t)0);
  891. X    return(now - ndays*DAY);
  892. X}
  893. X
  894. X/*
  895. X - printlists - print control lists for debugging
  896. X */
  897. Xvoid
  898. Xprintlists()
  899. X{
  900. X    register int i;
  901. X    register struct ctl *ct;
  902. X
  903. X    fprintf(stderr, "control file:\n");
  904. X    for (ct = ctls; ct != NULL; ct = ct->next)
  905. X        pctl(ct);
  906. X    fprintf(stderr, "\n");
  907. X
  908. X    for (i = 0; i < NHASH; i++)
  909. X        if (ngs[i] != NULL) {
  910. X            fprintf(stderr, "list %d:\n", i);
  911. X            for (ct = ngs[i]; ct != NULL; ct = ct->next)
  912. X                pctl(ct);
  913. X        }
  914. X    fprintf(stderr, "\n");
  915. X}
  916. X
  917. X/*
  918. X - pctl - print one control-list entry
  919. X */
  920. Xvoid
  921. Xpctl(ct)
  922. Xregister struct ctl *ct;
  923. X{
  924. X#    define    DAYS(x)    ((now-(x))/(double)DAY)
  925. X
  926. X    fprintf(stderr, "%s(%c) %.1f-%.1f-%.1f %s\n", ct->groups, ct->ismod,
  927. X            DAYS(ct->retain), DAYS(ct->normal), DAYS(ct->purge),
  928. X            (ct->dir == NULL) ? "(null)" : ct->dir);
  929. X}
  930. X
  931. X/*
  932. X - unprivileged - no-op needed to keep the pathname stuff happy
  933. X */
  934. Xvoid
  935. Xunprivileged()
  936. X{
  937. X}
  938. END_OF_FILE
  939. if test 20350 -ne `wc -c <'expire/expire.c'`; then
  940.     echo shar: \"'expire/expire.c'\" unpacked with wrong size!
  941. fi
  942. # end of 'expire/expire.c'
  943. fi
  944. if test -f 'rna/postnews.c' -a "${1}" != "-c" ; then 
  945.   echo shar: Will not clobber existing file \"'rna/postnews.c'\"
  946. else
  947. echo shar: Extracting \"'rna/postnews.c'\" \(20673 characters\)
  948. sed "s/^X//" >'rna/postnews.c' <<'END_OF_FILE'
  949. X/*
  950. X * postnews [-h] [-s subject] [-n newsgroups] [-e expiredate]
  951. X *         [-r references] [-i interpfile] [-d distribution]
  952. X * postnews -c control_command [-n newsgroups] [-d distribution]
  953. X * postnews -p
  954. X *
  955. X *    SETUID to NEWSROOT "news"
  956. X *
  957. X *    Michael Rourke (UNSW) April 1984
  958. X */
  959. X
  960. X#include "defs.h"
  961. X
  962. Xchar sys[]         = SYS;
  963. Xextern char mydomain[];
  964. Xextern char newsversion[];
  965. X#ifndef NETID
  966. Xchar systemid[DIRSIZ];
  967. X#else
  968. Xchar systemid[] = NETID;
  969. X#endif
  970. X#ifdef UUNAME
  971. Xchar uuname[] = UUNAME;
  972. X#endif
  973. X
  974. Xchar *sflag;        /* subject */
  975. Xchar *nflag;        /* newsgroups */
  976. Xchar *dflag;        /* distribution */
  977. Xlong eflag;        /* expire date */
  978. Xchar *cflag;        /* control function */
  979. Xchar *rflag;        /* references */
  980. Xchar *iflag;        /* interp file */
  981. Xbool        hflag;        /* headers in input */
  982. Xbool        pflag;        /* from other host (su only) */
  983. Xbool        tty;        /* isatty */
  984. X
  985. X#if AUSAM
  986. Xstruct pwent pe;        /* current user passwd struct */
  987. Xchar sbuf[SSIZ];    /* strings thereof */
  988. X#else
  989. Xstruct passwd *pp;        /* current user passwd struct */
  990. X#endif
  991. Xlong now;        /* current time */
  992. Xint oumask;        /* old umask */
  993. Xheader        h;        /* current articles header */
  994. Xbool        su;        /* if news super-user */
  995. Xchar *tname;        /* name of temp file */
  996. XFILE        *tmp;        /* file ptr */
  997. Xchar *newsdir;    /* %news */
  998. Xuid_t        newsuid;    /* news uid */
  999. X
  1000. Xint checkng(), linkng();
  1001. Xchar *dfltgrp();
  1002. Xextern FILE    *mailreply(), *mailnewsroot();
  1003. Xextern bool    chkhist(), cancel();
  1004. X
  1005. Xmain(argc, argv)
  1006. Xint argc;
  1007. Xchar *argv[];
  1008. X{
  1009. X    register int i;
  1010. X    extern long maketime();
  1011. X
  1012. X    time(&now);
  1013. X    for (argv++, argc--; argc > 0; argc--, argv++) {
  1014. X        if (argv[0][0] != '-' && argv[0][2] != '\0')
  1015. X            break;
  1016. X        switch (argv[0][1]) {
  1017. X        case 'h':
  1018. X            hflag = true;
  1019. X            continue;
  1020. X        case 'p':
  1021. X            pflag = true;
  1022. X            continue;
  1023. X        case 's':
  1024. X            sflag = argv[1];
  1025. X            break;
  1026. X        case 'n':
  1027. X            nflag = argv[1];
  1028. X            break;
  1029. X        case 'r':
  1030. X            rflag = argv[1];
  1031. X            break;
  1032. X        case 'i':
  1033. X            iflag = argv[1];
  1034. X            break;
  1035. X        case 'd':
  1036. X            dflag = argv[1];
  1037. X            break;
  1038. X        case 'e':
  1039. X            for (i = 1; i < argc; i++)
  1040. X                if (argv[i][0] == '-' && argv[i][2] == '\0')
  1041. X                    break;
  1042. X            if (i == 1) {
  1043. X                argc = -1;
  1044. X                break;
  1045. X            }
  1046. X            i--;
  1047. X            if ((eflag = maketime(i, &argv[1], TIMES)) == 0L)
  1048. X                exit(1);
  1049. X            argc -= i, argv += i;
  1050. X            continue;
  1051. X        case 'c':
  1052. X            cflag = argv[1];
  1053. X            break;
  1054. X        default:
  1055. X            argc = -1;
  1056. X            break;
  1057. X        }
  1058. X        argv++, argc--;
  1059. X    }
  1060. X    if (argc != 0 || (cflag && pflag) || (pflag && (nflag || dflag)) ||
  1061. X        ((cflag || pflag) && (hflag || sflag || eflag || rflag || iflag))) {
  1062. X        fprintf(stderr, "Usage: postnews [-h] [-s subject] [-n newsgroups] [-e expiredate ..]\n");
  1063. X        fprintf(stderr, "                [-r references] [-i interpfile] [-d distribution]\n");
  1064. X        fprintf(stderr, "       postnews -c control_command [-n newsgroups] [-d distribution]\n");
  1065. X        fprintf(stderr, "       postnews -p\n");
  1066. X        exit(1);
  1067. X    }
  1068. X    oumask = umask(022);
  1069. X
  1070. X#if AUSAM
  1071. X    pe.pw_strings[LNAME] = NEWSROOT;
  1072. X    if (getpwuid(&pe, sbuf, sizeof(sbuf)) == PWERROR)
  1073. X        error("Password file error.");
  1074. X    newsdir = newstr(pe.pw_strings[DIRPATH]);
  1075. X    newsuid = pe.pw_limits.l_uid;
  1076. X#else
  1077. X    if ((pp = getpwnam(NEWSROOT)) == (struct passwd *) NULL)
  1078. X        error("Password file error.");
  1079. X    newsdir = newstr(pp->pw_dir);
  1080. X    newsuid = pp->pw_uid;
  1081. X#endif
  1082. X
  1083. X#if AUSAM
  1084. X    pe.pw_limits.l_uid = getuid();
  1085. X    if (getpwlog(&pe, sbuf, sizeof(sbuf)) == PWERROR)
  1086. X        error("Password file error.");
  1087. X    pwclose();
  1088. X    su = (bool) (pe.pw_limits.l_uid == 0 || pe.pw_limits.l_uid == newsuid);
  1089. X#else
  1090. X    if ((pp = getpwuid(getuid())) == (struct passwd *) NULL)
  1091. X        error("Password file error.");
  1092. X    endpwent();
  1093. X    su = (bool) ((pp->pw_uid == 0) || (pp->pw_uid = newsuid));
  1094. X#endif
  1095. X
  1096. X#ifndef NETID
  1097. X    getaddr(G_SYSNAME, systemid);
  1098. X#endif
  1099. X    tty = (bool) isatty(fileno(stdin));
  1100. X
  1101. X#if AUSAM
  1102. X    if (!su && (pe.pw_limits.l_flags & USENET) == 0)
  1103. X        error("Net permission is required to post news.");
  1104. X#endif
  1105. X
  1106. X    if (!su && pflag)
  1107. X        error("Permission denied.");
  1108. X
  1109. X    if (pflag || hflag)
  1110. X        gethead(stdin, &h);
  1111. X    if (sflag)
  1112. X        h.h_subject = sflag;
  1113. X    if (cflag) {
  1114. X        h.h_control = cflag;
  1115. X        h.h_subject = "Control";
  1116. X        if (!h.h_replyto)
  1117. X            h.h_replyto = newstr5(
  1118. X#if AUSAM
  1119. X                    pe.pw_strings[LNAME],
  1120. X#else
  1121. X                    pp->pw_name,
  1122. X#endif
  1123. X                "@", systemid, ".", mydomain);
  1124. X    }
  1125. X    if (nflag)
  1126. X        h.h_newsgroups = nflag;
  1127. X    if (rflag)
  1128. X        h.h_references = rflag;
  1129. X    if (eflag)
  1130. X        if (eflag < now)
  1131. X            error("Time specified has passed.");
  1132. X        else
  1133. X            h.h_expires = newstr(ttoa(eflag));
  1134. X    if (dflag)
  1135. X        h.h_distribution = dflag;
  1136. X    if (tty)
  1137. X        askheads();
  1138. X    if (h.h_newsgroups)
  1139. X        convgrps(h.h_newsgroups);
  1140. X    else
  1141. X        h.h_newsgroups = DFLTGRP;
  1142. X    if (h.h_distribution) {
  1143. X        convgrps(h.h_distribution);
  1144. X        if (CMP(h.h_newsgroups, h.h_distribution) == 0)
  1145. X            h.h_distribution = NIL(char);
  1146. X    }
  1147. X    if (!h.h_subject)
  1148. X        error("No subject specified.");
  1149. X    if (pflag) {
  1150. X        if (chkhist(h.h_messageid))
  1151. X            error("Duplicate article %s rejected.", h.h_messageid);
  1152. X    } else
  1153. X     {
  1154. X        if (h.h_relayversion || h.h_postversion || h.h_from || h.h_date ||
  1155. X            h.h_messageid || h.h_path || h.h_sender || h.h_datereceived ||
  1156. X            h.h_lines)
  1157. X            error("Illegally specified field(s).");
  1158. X
  1159. X        if (!su && ngmatch(h.h_newsgroups, MODGROUPS))
  1160. X            error("Moderated newsgroup:\n\tArticle must be mailed to the newsgroup moderator.");
  1161. X
  1162. X        if (!cflag && applyng(h.h_newsgroups, checkng))
  1163. X            exit(1);
  1164. X        if (!cflag && h.h_distribution && applyng(h.h_distribution,
  1165. X            checkng))
  1166. X            exit(1);
  1167. X    }
  1168. X    install(&h, stdin);
  1169. X    exit(0);
  1170. X}
  1171. X
  1172. X
  1173. X/*
  1174. X * create all the directories required for a given group
  1175. X */
  1176. Xcreatgroup(grp)
  1177. Xchar *grp;
  1178. X{
  1179. X    register char *dname;
  1180. X    register char *s, *slash;
  1181. X
  1182. X    if (strpbrk(grp, BADGRPCHARS))
  1183. X        error("%s: Illegal char in newsgroup.", grp);
  1184. X    initgrp(grp);        /* make entry in active file */
  1185. X    dname = convg(newstr3(newsdir, "/", grp));
  1186. X    s = dname + strlen(newsdir);
  1187. X    while (*s) {
  1188. X        slash = strchr(s, '/');
  1189. X        if (slash)
  1190. X            *slash = '\0';
  1191. X        if (access(dname, 0) != 0)
  1192. X            mkdir(dname);
  1193. X        if (slash)
  1194. X            *slash = '/', s = slash + 1;
  1195. X        else
  1196. X            break;
  1197. X    }
  1198. X    free(dname);
  1199. X}
  1200. X
  1201. X
  1202. X/*
  1203. X * create dir
  1204. X */
  1205. Xmkdir(s)
  1206. Xchar *s;
  1207. X{
  1208. X    int pid, status, r;
  1209. X    int (*istat)(), (*qstat)();
  1210. X
  1211. X    switch (pid = fork()) {
  1212. X    default:
  1213. X        /* parent */
  1214. X        break;
  1215. X    case 0:
  1216. X        /* child */
  1217. X        execl(MKDIR, "mkdir", s, 0);
  1218. X        error("Can't exec %s", MKDIR);
  1219. X        exit(1);
  1220. X    case -1:
  1221. X        error("Can't fork.");
  1222. X    }
  1223. X
  1224. X    istat = signal(SIGINT, SIG_IGN);
  1225. X    qstat = signal(SIGQUIT, SIG_IGN);
  1226. X
  1227. X    while ((r = wait(&status)) != pid && r != -1)
  1228. X        ;
  1229. X    if (r == -1 || status)
  1230. X        error("Couldn't mkdir %s", s);
  1231. X
  1232. X    if (getuid() == 0) {
  1233. X        switch (pid = fork()) {
  1234. X        default:
  1235. X            /* parent */
  1236. X            break;
  1237. X        case 0:
  1238. X            /* child */
  1239. X            setgid(0);
  1240. X            setuid(0);
  1241. X            execl(CHOWN, "chown", NEWSROOT, s, 0);
  1242. X            error("Can't exec %s", CHOWN);
  1243. X            exit(1);
  1244. X        case -1:
  1245. X            error("Can't fork.");
  1246. X        }
  1247. X        while ((r = wait(&status)) != pid && r != -1)
  1248. X            ;
  1249. X        if (r == -1 || status)
  1250. X            error("Couldn't chown %s", s);
  1251. X    }
  1252. X
  1253. X    chmod(s, 0755);
  1254. X
  1255. X    signal(SIGINT, istat);
  1256. X    signal(SIGQUIT, qstat);
  1257. X}
  1258. X
  1259. X
  1260. X/*
  1261. X * get unset headers from stdin
  1262. X */
  1263. Xaskheads()
  1264. X{
  1265. X    static char form[]     = "%s: %s\n";
  1266. X
  1267. X    char *geth();
  1268. X
  1269. X    extern char t_subject[], t_newsgroups[], t_distribution[];
  1270. X    extern char t_references[], t_expires[];
  1271. X
  1272. X    if (h.h_subject)
  1273. X        printf(form, t_subject, h.h_subject);
  1274. X    else
  1275. X        h.h_subject = geth(t_subject, NIL(char));
  1276. X    if (h.h_newsgroups)
  1277. X        printf(form, t_newsgroups, h.h_newsgroups);
  1278. X    else
  1279. X        h.h_newsgroups = geth(t_newsgroups, dfltgrp());
  1280. X    if (h.h_distribution)
  1281. X        printf(form, t_distribution, h.h_distribution);
  1282. X    else
  1283. X        h.h_distribution = geth(t_distribution, h.h_newsgroups);
  1284. X    if (h.h_expires)
  1285. X        printf(form, t_expires, h.h_expires);
  1286. X    if (h.h_references)
  1287. X        printf(form, t_references, h.h_references);
  1288. X}
  1289. X
  1290. X
  1291. X/*
  1292. X * get a header from stdin
  1293. X */
  1294. Xchar *
  1295. Xgeth(fname, def)
  1296. Xchar *fname, *def;
  1297. X{
  1298. X    register char *s;
  1299. X
  1300. X    while (1) {
  1301. X        if (def)
  1302. X            printf("%s (%s): ", fname, def);
  1303. X        else
  1304. X            printf("%s: ", fname);
  1305. X        if ((s = mgets()) && *s)
  1306. X            return newstr(s);
  1307. X        if (def)
  1308. X            return newstr(def);
  1309. X        printf("%s field is mandatory.\n", fname);
  1310. X    }
  1311. X}
  1312. X
  1313. X
  1314. X/*
  1315. X * install the news item
  1316. X */
  1317. Xinstall(hp, f)
  1318. Xregister header *hp;
  1319. XFILE *f;
  1320. X{
  1321. X    register FILE    *sf;
  1322. X    register int c;
  1323. X    register char *mach, *subs, *type, *com, *end;
  1324. X    char buf[BUFLEN];
  1325. X
  1326. X    int cleanup();
  1327. X    FILE         * getbody(), *trimit();
  1328. X
  1329. X    signal(SIGINT, cleanup);
  1330. X    signal(SIGQUIT, cleanup);
  1331. X
  1332. X    if (tty && !cflag)
  1333. X        f = getbody(f);
  1334. X
  1335. X    if (!hp->h_lines && !cflag)
  1336. X        f = trimit(hp, f);
  1337. X
  1338. X    if ((tname = mtempnam(newsdir, "itmp")) == NIL(char) || (tmp = fopen(tname,
  1339. X         "w+")) == NIL(FILE))
  1340. X        error("Can't create itmp file.");
  1341. X
  1342. X    chown(tname, (int) newsuid, (int) newsuid);    /* in case we are currently root */
  1343. X
  1344. X    if (pflag)
  1345. X        puthead(hp, tmp, passing);
  1346. X    else
  1347. X     {
  1348. X        h.h_messageid = newstr5("<", getunique(), "@", systemid, ".");
  1349. X        h.h_messageid = catstr2(h.h_messageid, mydomain, ">");
  1350. X        puthead(hp, tmp, making);
  1351. X    }
  1352. X
  1353. X    putc('\n', tmp);
  1354. X    if (!cflag)
  1355. X        while ((c = getc(f)) != EOF)
  1356. X            putc(c, tmp);
  1357. X
  1358. X    signal(SIGINT, SIG_IGN);
  1359. X    signal(SIGQUIT, SIG_IGN);
  1360. X
  1361. X    sf = fopenf(sys, "r");
  1362. X    while (fgets(buf, sizeof(buf), sf)) {
  1363. X        mach = buf;
  1364. X        if ((subs = strchr(buf, ':')) == NIL(char) || (type = strchr(subs +
  1365. X            1, ':')) == NIL(char) || (com = strchr(type + 1, ':')) == NIL(char)
  1366. X            || (end = strchr(com + 1, '\n')) == NIL(char))
  1367. X            error("%s: Bad format.", sys);
  1368. X        *subs++ = *type++ = *com++ = *end = '\0';
  1369. X        if ((hp->h_distribution && ngmatch(hp->h_distribution, subs)
  1370. X            || !hp->h_distribution) && ngmatch(hp->h_newsgroups, subs))
  1371. X            if (CMP(mach, systemid) == 0)
  1372. X                local(hp, tname, subs);
  1373. X            else if ((pflag && checkpath(hp->h_path, mach) || !pflag)
  1374. X                && remote(tmp, com))
  1375. X                printf("Couldn't transmit to %s\n", mach);
  1376. X    }
  1377. X    fclose(sf);
  1378. X    fclose(tmp);
  1379. X    unlink(tname);
  1380. X}
  1381. X
  1382. X
  1383. X/*
  1384. X * have to count the number of lines
  1385. X * and trim leading and trailing empty lines
  1386. X */
  1387. XFILE *
  1388. Xtrimit(hp, f)
  1389. Xheader *hp;
  1390. XFILE *f;
  1391. X{
  1392. X    register int ccount, lcount, llcount;
  1393. X    register int c, lastc;
  1394. X
  1395. X    ccount = lcount = llcount = 0;
  1396. X    if ((tmp = tmpfile()) == NIL(FILE))
  1397. X        error("Can't create tempfile.");
  1398. X    while ((c = getc(f)) != EOF && c == '\n')
  1399. X        ;
  1400. X    lastc = c;
  1401. X    if (c != EOF)
  1402. X        do
  1403. X         {
  1404. X            if (c == '\n' && lastc == '\n') {
  1405. X                llcount++;
  1406. X                continue;
  1407. X            }
  1408. X            while (llcount > 0) {
  1409. X                putc('\n', tmp);
  1410. X                lcount++;
  1411. X                llcount--;
  1412. X            }
  1413. X            if (isprint(c))
  1414. X                putc(c, tmp), ccount++;
  1415. X            else if (isspace(c) || c == '\b') {
  1416. X                if (c == '\n')
  1417. X                    lcount++;
  1418. X                putc(c, tmp);
  1419. X            } else
  1420. X                putc('?', tmp);
  1421. X            lastc = c;
  1422. X        } while ((c = getc(f)) != EOF);
  1423. X    if (ccount < 5 && !pflag)
  1424. X        error("Article too short.");
  1425. X    hp->h_lines = newstr(itoa(lcount));
  1426. X    rewind(tmp);
  1427. X    fclose(f);
  1428. X    return tmp;
  1429. X}
  1430. X
  1431. X
  1432. X/*
  1433. X * input new article interactivly
  1434. X * and place on standard input
  1435. X */
  1436. XFILE *
  1437. Xgetbody(f)
  1438. XFILE *f;
  1439. X{
  1440. X    register int c;
  1441. X    char buf[BUFSIZ];
  1442. X    bool        quit;
  1443. X
  1444. X    printf("\n");
  1445. X    quit = false;
  1446. X    if ((tname = mtempnam(newsdir, "itmp")) == NIL(char) || (tmp = fopen(tname,
  1447. X         "w+")) == NIL(FILE))
  1448. X        error("Can't create itmp file.");
  1449. X    /*
  1450. X     * read article text, interpreting escape commands
  1451. X     */
  1452. X    while (!quit && (c = getc(f)) != EOF)
  1453. X        if (c == '.' || c == '!') {
  1454. X            switch (c = (c == '!' ? c : getc(f))) {
  1455. X            case 'e':
  1456. X                fclose(tmp);
  1457. X                readln(f);
  1458. X                doed(tname);
  1459. X                if ((tmp = fopen(tname, "a+")) == NIL(FILE))
  1460. X                    error("Can't re-open %s", tname);
  1461. X                break;
  1462. X            case 'i':
  1463. X                if (!iflag) {
  1464. X                    readln(f);
  1465. X                    printf("Can't interpolate.\n");
  1466. X                    break;
  1467. X                }
  1468. X                icopy(iflag, tmp);
  1469. X                break;
  1470. X            case '!':
  1471. X                msystem(mgets());
  1472. X                break;
  1473. X            case '\n':
  1474. X                quit = true;
  1475. X                continue;
  1476. X            default:
  1477. X                printf("Unknown escape command: \"%c\"\n", c);
  1478. X                printf("Commands are:\n");
  1479. X                printf("\t.e          - edit article so far\n");
  1480. X                printf("\t.i          - interpolate article\n");
  1481. X                printf("\t.!command   - shell escape\n");
  1482. X                readln(f);
  1483. X            }
  1484. X            printf("(continue)\n");
  1485. X            fflush(stdout);
  1486. X        }
  1487. X        else
  1488. X         {
  1489. X            if (c == '\\' && (c = getc(f)) != '.')
  1490. X                fprintf(tmp, "\\");
  1491. X            fprintf(tmp, "%c", c);
  1492. X            if (c != '\n' && fgets(buf, sizeof buf, f) != NIL(char))
  1493. X                fputs(buf, tmp);
  1494. X        }
  1495. X    rewind(tmp);
  1496. X    fclose(f);
  1497. X    unlink(tname);
  1498. X    free(tname);
  1499. X    tname = NIL(char);
  1500. X    return tmp;
  1501. X}
  1502. X
  1503. X
  1504. X/*
  1505. X * interpolate fname
  1506. X */
  1507. Xicopy(fname, to)
  1508. Xchar *fname;
  1509. XFILE *to;
  1510. X{
  1511. X    register FILE    *f;
  1512. X    register int c, lastc;
  1513. X
  1514. X    if ((f = fopen(fname, "r")) == NIL(FILE)) {
  1515. X        perror(fname);
  1516. X        return;
  1517. X    }
  1518. X    lastc = '\n';
  1519. X    while ((c = getc(f)) != EOF) {
  1520. X        if (lastc == '\n')
  1521. X            fprintf(to, "    ");
  1522. X        putc(lastc = c, to);
  1523. X    }
  1524. X    fclose(f);
  1525. X}
  1526. X
  1527. X
  1528. X/*
  1529. X * invoke an editor on fname
  1530. X */
  1531. Xdoed(fname)
  1532. Xchar *fname;
  1533. X{
  1534. X    register int i;
  1535. X    register char *editor;
  1536. X    register char *edname;
  1537. X    int pid, stat;
  1538. X    extern char *getenv();
  1539. X    static char ed[]          = ED;
  1540. X
  1541. X    chmod(fname, 0660);
  1542. X    if ((pid = fork()) == 0) {
  1543. X        setuid(getuid());
  1544. X        if ((editor = getenv("EDITOR")) == NIL(char))
  1545. X            editor = ed;
  1546. X        edname = strrchr(editor, '/');
  1547. X        edname = (edname ? edname + 1 : editor);
  1548. X        execl(editor, edname, fname, 0);
  1549. X        perror(editor);
  1550. X        exit(1);
  1551. X    }
  1552. X    if (pid == -1) {
  1553. X        error("Can't fork ed.");
  1554. X        return;
  1555. X    }
  1556. X    signal(SIGINT, SIG_IGN);
  1557. X    signal(SIGQUIT, SIG_IGN);
  1558. X
  1559. X    while ((i = wait(&stat)) != pid && i != -1)
  1560. X        ;
  1561. X
  1562. X    chmod(fname, 0644);
  1563. X
  1564. X    signal(SIGINT, cleanup);
  1565. X    signal(SIGQUIT, cleanup);
  1566. X}
  1567. X
  1568. X
  1569. X/*
  1570. X * remove temp file, and perhaps save in $HOME/dead.article
  1571. X */
  1572. Xcleanup(sig)
  1573. Xint sig;
  1574. X{
  1575. X    if (sig)
  1576. X        printf("\nInterrupt\n");
  1577. X    signal(SIGINT, SIG_IGN);
  1578. X    signal(SIGQUIT, SIG_IGN);
  1579. X    if (tname && *tname) {
  1580. X        unlink(tname);
  1581. X        tname = NIL(char);
  1582. X        if (tty && tmp)
  1583. X            saveit();
  1584. X    }
  1585. X    exit(1);
  1586. X}
  1587. X
  1588. X
  1589. X/*
  1590. X * save the current temporary file
  1591. X * in $HOME/dead.article
  1592. X */
  1593. Xsaveit()
  1594. X{
  1595. X    register FILE    *f;
  1596. X    register char *nname;
  1597. X    register int c;
  1598. X
  1599. X    extern char *getenv();
  1600. X
  1601. X    rewind(tmp);
  1602. X    if ((nname = getenv("HOME")) == NIL(char))
  1603. X        return;
  1604. X    nname = newstr2(nname, "/dead.article");
  1605. X
  1606. X    umask(oumask);
  1607. X    setgid(getgid());
  1608. X    setuid(getuid());
  1609. X
  1610. X    f = fopenf(nname, "w");
  1611. X    h.h_messageid = NIL(char);
  1612. X    puthead(&h, f, printing);
  1613. X    putc('\n', f);
  1614. X    while ((c = getc(tmp)) != EOF)
  1615. X        putc(c, f);
  1616. X    fclose(f);
  1617. X    printf("Article saved in %s\n", nname);
  1618. X    free(nname);
  1619. X}
  1620. X
  1621. X
  1622. X/*
  1623. X * check that mach is not in path
  1624. X * so we won't send it back again
  1625. X */
  1626. Xcheckpath(path, mach)
  1627. Xchar *path;
  1628. Xchar *mach;
  1629. X{
  1630. X    register char *ex;
  1631. X    register int r;
  1632. X
  1633. X    while (*path && (ex = strchr(path, PSEPCHAR))) {
  1634. X        *ex = '\0';
  1635. X        r = (CMP(path, mach) == 0);
  1636. X        *ex = PSEPCHAR;
  1637. X        if (r)
  1638. X            return 0;
  1639. X        path = ex + 1;
  1640. X    }
  1641. X    return 1;
  1642. X}
  1643. X
  1644. X
  1645. X/* VARARGS1 */
  1646. Xerror(s, a0, a1, a2, a3)
  1647. Xchar *s;
  1648. X{
  1649. X    fprintf(stderr, "postnews: ");
  1650. X    fprintf(stderr, s, a0, a1, a2, a3);
  1651. X    fprintf(stderr, "\n");
  1652. X    cleanup(0);
  1653. X}
  1654. X
  1655. X
  1656. X/*
  1657. X * check that a newsgroup exists
  1658. X */
  1659. Xcheckng(g)
  1660. Xchar *g;
  1661. X{
  1662. X    register char *dname;
  1663. X    register char *s, *com;
  1664. X
  1665. X    dname = convg(newstr3(newsdir, "/", g));
  1666. X    free(dname);
  1667. X    if (access(dname, 0) != 0) {
  1668. X        if (su && tty) {
  1669. X            printf("%s: Nonexistent newgroup. Create? ", g);
  1670. X            if ((s = mgets()) == NIL(char) || !*s || (CMP(s, "y") !=
  1671. X                0 && CMP(s, "yes") != 0))
  1672. X                return 1;
  1673. X            com = newstr6("exec ", POSTNEWS, " -c 'newgroup ", g,
  1674. X                                  "' -n ", g);
  1675. X            system(com);
  1676. X            free(com);
  1677. X            return 0;
  1678. X        }
  1679. X        printf("%s: Nonexistent newsgroup.\n", g);
  1680. X        return 1;
  1681. X    }
  1682. X    return 0;
  1683. X}
  1684. X
  1685. X
  1686. X/*
  1687. X * place news locally
  1688. X * by linking tmp file into each newsgroup directory
  1689. X */
  1690. Xlocal(hp, tmpname, subs)
  1691. Xheader *hp;
  1692. Xchar *tmpname;
  1693. Xchar *subs;
  1694. X{
  1695. X    static char junk[] = "junk";
  1696. X
  1697. X    register char *newg;
  1698. X    register FILE    *pp;
  1699. X
  1700. X    if (hp->h_control)
  1701. X        control(hp->h_control);
  1702. X    else if (newg = ngsquash(hp->h_newsgroups, subs)) {
  1703. X        openhist(hp);
  1704. X        if (!applyng(newg, linkng, tmpname)) {
  1705. X            while (!linkng(junk, tmpname))
  1706. X                creatgroup(junk);
  1707. X            pp = mailnewsroot("Postnews: Article placed in \"junk\"");
  1708. X            fprintf(pp, "Article placed in \"junk\" because no groups active from:\n\t%s\n",
  1709. X                                 newg);
  1710. X            mailclose(pp);
  1711. X        }
  1712. X        closehist();
  1713. X        free(newg);
  1714. X    } else
  1715. X        error("Snark: in local");
  1716. X}
  1717. X
  1718. X
  1719. X/*
  1720. X * link news items into local dirs
  1721. X */
  1722. Xlinkng(g, t)
  1723. Xchar *g, *t;
  1724. X{
  1725. X    register char *lname, *dname;
  1726. X    extern char *getseq();
  1727. X
  1728. X    dname = convg(newstr3(newsdir, "/", g));
  1729. X    free(dname);
  1730. X    if (access(dname, 0) != 0)
  1731. X        return 0;        /* don't save - not an active group */
  1732. X    dname = convg(newstr3(g, "/#", getseq(g)));
  1733. X    writehist(dname);
  1734. X    lname = newstr3(newsdir, "/", dname);
  1735. X    if (link(t, lname) != 0)
  1736. X        error("Link from %s to %s failed.", t, lname);
  1737. X    free(lname);
  1738. X    free(dname);
  1739. X    return 1;
  1740. X}
  1741. X
  1742. X
  1743. X/*
  1744. X * execute control messages
  1745. X */
  1746. Xcontrol(com)
  1747. Xchar *com;
  1748. X{
  1749. X    register char *s;
  1750. X    register FILE    *pp;
  1751. X
  1752. X    if (s = strchr(com, ' ')) {
  1753. X        *s++ = '\0';
  1754. X        while (*s && isspace(*s))
  1755. X            s++;
  1756. X    }
  1757. X    if (CMP(com, "cancel") == 0) {
  1758. X        if (!s || !*s)
  1759. X            error("cancel: message-id not specified.");
  1760. X        cancel(s);
  1761. X    } else if (CMP(com, "newgroup") == 0 || CMP(com, "rmgroup") == 0) {
  1762. X        if (!s || !*s)
  1763. X            error("%s: group name not specified.", com);
  1764. X        if (!su)
  1765. X            error("%s: permission denied.", com);
  1766. X        else if (CMP(com, "newgroup") == 0)
  1767. X            creatgroup(s);
  1768. X        else
  1769. X         {
  1770. X            pp = mailnewsroot("Postnews: rmgroup request");
  1771. X            fprintf(pp, "rmgroup %s, sent from %s\n", s, (h.h_replyto ?
  1772. X                h.h_replyto : (h.h_from ? h.h_from  : "Unknown?")));
  1773. X            fprintf(pp, "run '/usr/lib/news/rmgroup %s' if sender is authorised.\n",
  1774. X                 s);
  1775. X            mailclose(pp);
  1776. X        }
  1777. X    } else if (CMP(com, "sendsys") == 0)
  1778. X        sendsys();
  1779. X    else if (CMP(com, "version") == 0)
  1780. X        version();
  1781. X    else if (CMP(com, "senduuname") == 0)
  1782. X        senduuname();
  1783. X    else
  1784. X     {
  1785. X        sorry(com);
  1786. X        error("Unknown control command: %s %s\n(Valid: cancel, newgroup, sendsys, senduuname, version)\nSent from: %s",
  1787. X                         com, s ? s : "", (h.h_replyto ? h.h_replyto : (h.h_from ?
  1788. X            h.h_from : "Unknown?")));
  1789. X    }
  1790. X}
  1791. X
  1792. X
  1793. X/*
  1794. X * send sys file to originator
  1795. X */
  1796. Xsendsys()
  1797. X{
  1798. X    register FILE    *pp, *fp;
  1799. X    register int c;
  1800. X
  1801. X    fp = fopenf(sys, "r");
  1802. X    pp = mailreply("News sendsys request");
  1803. X    while ((c = getc(fp)) != EOF)
  1804. X        putc(c, pp);
  1805. X    mailclose(pp);
  1806. X    fclose(fp);
  1807. X}
  1808. X
  1809. X
  1810. X/*
  1811. X * send version name and number to originator
  1812. X */
  1813. Xversion()
  1814. X{
  1815. X    register FILE    *pp;
  1816. X
  1817. X    pp = mailreply("News version request");
  1818. X    fprintf(pp, "Current version: %s\n", newsversion);
  1819. X    mailclose(pp);
  1820. X    fclose(pp);
  1821. X}
  1822. X
  1823. X
  1824. X/*
  1825. X * send uuname data to originator
  1826. X */
  1827. Xsenduuname()
  1828. X{
  1829. X    register FILE    *pp, *f;
  1830. X    register int c;
  1831. X
  1832. X    extern FILE    *tmpfile();
  1833. X
  1834. X#ifndef UUNAME
  1835. X    sorry("uuname");
  1836. X#else
  1837. X    if ((pp = popen(uuname, "r")) == NIL(FILE))
  1838. X        error("Couldn't run \"%s\"", uuname);
  1839. X    if ((f = tmpfile()) == NIL(FILE))
  1840. X        error("Can't open tmp file.");
  1841. X
  1842. X    while ((c = getc(pp)) != EOF)
  1843. X        putc(c, f);
  1844. X
  1845. X    if (pclose(pp) != 0)
  1846. X        error("\"%s\" had bad exit status.", uuname);
  1847. X    rewind(f);
  1848. X
  1849. X    pp = mailreply("News senduuname request");
  1850. X    while ((c = getc(f)) != EOF)
  1851. X        putc(c, pp);
  1852. X
  1853. X    fclose(f);
  1854. X    mailclose(pp);
  1855. X#endif
  1856. X}
  1857. X
  1858. X
  1859. X/*
  1860. X * send message about unimplemented command
  1861. X */
  1862. Xsorry(com)
  1863. Xchar *com;
  1864. X{
  1865. X    register FILE    *pp;
  1866. X
  1867. X    pp = mailreply("Unimplemented news control message");
  1868. X    fprintf(pp, "The control message \"%s\" is not implemented at this site.\n",
  1869. X         com);
  1870. X    fprintf(pp, "Our current version of news is: %s\n", newsversion);
  1871. X    mailclose(pp);
  1872. X}
  1873. X
  1874. X
  1875. X/*
  1876. X * set up a pipe to a mail program to reply to control requests
  1877. X */
  1878. XFILE *
  1879. Xmailreply(s)
  1880. Xchar *s;
  1881. X{
  1882. X    register FILE    *pp;
  1883. X    register char *com, *ra;
  1884. X
  1885. X    if ((ra = getretaddr(&h)) == NIL(char))
  1886. X        error("Can't form return address for control message");
  1887. X    com = newstr4("exec ", MAIL, " ", ra);
  1888. X    if ((pp = popen(com, "w")) == NIL(FILE))
  1889. X        error("Couldn't run \"%s\"", com);
  1890. X    fprintf(pp, "Subject: %s\n", s);
  1891. X    fprintf(pp, "Responding-system: %s.%s\n\n", systemid, mydomain);
  1892. X    free(com);
  1893. X    free(ra);
  1894. X    return pp;
  1895. X}
  1896. X
  1897. X
  1898. X/*
  1899. X * set up a pipe to mail to NEWSROOT
  1900. X */
  1901. XFILE *
  1902. Xmailnewsroot(s)
  1903. Xchar *s;
  1904. X{
  1905. X    register FILE    *pp;
  1906. X    register char *com;
  1907. X
  1908. X    com = newstr4("exec ", MAIL, " ", NEWSROOT);
  1909. X    if ((pp = popen(com, "w")) == NIL(FILE))
  1910. X        error("Couldn't run \"%s\"", com);
  1911. X    fprintf(pp, "Subject: %s\n", s);
  1912. X    free(com);
  1913. X    return pp;
  1914. X}
  1915. X
  1916. X
  1917. X/*
  1918. X * close the mail pipe
  1919. X */
  1920. Xmailclose(pp)
  1921. XFILE *pp;
  1922. X{
  1923. X    if (pclose(pp) != 0)
  1924. X        error("Mail failed");
  1925. X}
  1926. X
  1927. X
  1928. X/*
  1929. X * send item to remote hosts
  1930. X */
  1931. Xremote(f, com)
  1932. XFILE *f;
  1933. Xchar *com;
  1934. X{
  1935. X    register int c;
  1936. X    FILE         * out;
  1937. X
  1938. X    if ((out = popen(com, "w")) == NIL(FILE))
  1939. X        return 1;
  1940. X    rewind(f);
  1941. X    while ((c = getc(f)) != EOF)
  1942. X        putc(c, out);
  1943. X    return pclose(out);
  1944. X}
  1945. X
  1946. X
  1947. X/*
  1948. X * shell escape
  1949. X */
  1950. Xmsystem(s)
  1951. Xchar *s;
  1952. X{
  1953. X    int status, pid, w;
  1954. X
  1955. X    if ((pid = fork()) == 0) {
  1956. X        close(fileno(tmp));
  1957. X        /*
  1958. X          * remember we are setuid to NEWS so...
  1959. X         */
  1960. X        umask(oumask);
  1961. X        setgid(getgid());
  1962. X        setuid(getuid());
  1963. X        execl(SHELL, "sh", "-c", s, 0);
  1964. X        _exit(127);
  1965. X    }
  1966. X    signal(SIGINT, SIG_IGN);
  1967. X    signal(SIGQUIT, SIG_IGN);
  1968. X
  1969. X    while ((w = wait(&status)) != pid && w != -1)
  1970. X        ;
  1971. X    if (w == -1)
  1972. X        status = -1;
  1973. X
  1974. X    signal(SIGINT, cleanup);
  1975. X    signal(SIGQUIT, cleanup);
  1976. X    return(status);
  1977. X}
  1978. X
  1979. X
  1980. X/*
  1981. X * work out the default group for this user
  1982. X */
  1983. Xchar *
  1984. Xdfltgrp()
  1985. X{
  1986. X#if MANGRPS
  1987. X    register char **sp;
  1988. X    extern char **getclasses();
  1989. X
  1990. X    for (sp = getclasses(pe.pw_cmask); *sp; sp++) {
  1991. X        if (CMP(*sp, "C-Staff") == 0 || CMP(*sp, "System") == 0)
  1992. X            return "system";
  1993. X        if (CMP(*sp, "CLUBS") == 0)
  1994. X            return newstr2("general.club.", pe.pw_strings[LNAME]);
  1995. X        if (CMP(*sp, "Languages") == 0)
  1996. X            return newstr2("general.lang.", pe.pw_strings[LNAME]);
  1997. X        if (CMP(*sp, "Classaccount") == 0)
  1998. X            return newstr2("class.", pe.pw_strings[LNAME]);
  1999. X    }
  2000. X#endif
  2001. X    return DFLTGRP;
  2002. X}
  2003. END_OF_FILE
  2004. if test 20673 -ne `wc -c <'rna/postnews.c'`; then
  2005.     echo shar: \"'rna/postnews.c'\" unpacked with wrong size!
  2006. fi
  2007. # end of 'rna/postnews.c'
  2008. fi
  2009. echo shar: End of archive 13 \(of 14\).
  2010. ##  End of shell archive.
  2011. exit 0
  2012.